<?php

namespace app\admin\controller;

use app\helpers\Common;
use app\common\model\AdminMenu as Menu;
use app\BaseController as Controller;

use support\Request;
use support\View;
use think\facade\Db;
use think\facade\Cache;
use think\Exception;

class BaseController extends Controller
{
    protected $admin = ''; // Admin
    protected $menu = null; // Current Menu Obj
    protected $appName = 'common'; // App Name
    protected $model = ''; // Model Name
    protected $access = [];
    protected $indexView = 'list'; // Index View
    protected $addView = 'add'; // Add View
    protected $editView = 'edit'; // Edit View
    protected $rootline = []; // Root line id array
    protected $validate = [];
    protected $defaultWhere = []; // default where
    protected $defaultOrder = 'created_at DESC';
    protected $pageSize = ['20', '30', '50', '100', '200'];
    protected $primaryKey = 'id'; // primaryKey
    protected $columns = []; // columns
    protected $view = '';
    protected $redirect = '';

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * 初始化操作
     */
    protected function initialize()
    {
        $request = request();

        // 获取当前菜单
        $app = $request->app;
        $pathinfo = substr($request->path(), 1);
        $pathinfo = explode('/', $pathinfo);
        $controller = !empty($pathinfo[1]) ? str_replace('.html', '', $pathinfo[1]) : 'index';
        $action = !empty($pathinfo[2]) ? str_replace('.html', '', $pathinfo[2]) : 'index';
        $uriArr = [$app, $controller, $action];
        $this->menu = Menu::where('app', $uriArr[0])->where('controller', $uriArr[1])->where('action', $uriArr[2])->find();

        // 获取当前model
        //$this->model = $uriArr[1];
        $controllerArr = explode('\\', $request->controller);
        $this->model = strstr(end($controllerArr), 'Controller', true);
        View::assign('model', $this->model);

        // Check route auth
        $this->authCheck($request);

        // Get menu (初始化后台菜单)
        View::assign('sidebar', $this->sidebarHtml($this->menuJson()));
        // Get local location
        View::assign('bcn', $this->getLocation());

        $this->defaultWhere = $this->getDefaultWhere();
    }

    /*
     * Check route auth
     */
    private function authCheck($request)
    {
        $session_admin = session('admin');
        $this->admin = $session_admin;
        View::assign('adminInfo', $session_admin);
    }

    /**
     * Cache menu
     *
     * @param null $data
     * @return bool|null
     */
    public function menuCache($data = null)
    {
        if (empty($data)) {
            $data = Menu::getTree(0);
            // 缓存
            Cache::tag('admin')->set('admin_menu', $data, 3600 * 6);
        } else {
            Cache::tag('admin')->set('admin_menu', $data, 3600 * 6);
        }

        return $data;
    }

    /**
     * 菜单树状结构集合
     */
    public function menuJson()
    {
        $menu = Cache::get('admin_menu');
        if (!$menu) {
            $menu = $this->menuCache();
        }

        return $menu;
    }

    /**
     * 左侧菜单
     */
    public function sidebarHtml($menus)
    {
        if ($menus) {
            if (Cache::get('admin_access')) {
                $this->access = array_unique(Common::trimExplode(',', Cache::get('admin_access')));
            }
            $subMenu = function ($menus, $level = 0) use (&$subMenu) {
                $request = request();
                $output = '';
                foreach ($menus as $menu) {
                    // Check access
                    if ($this->admin['type'] != 0 && !in_array($menu['id'], $this->access)) {
                        continue;
                    }
                    /*if (!in_array($menu['id'], $this->access)) {
                        continue;
                    }*/
                    $app = $request->app;
                    $pathinfo = substr($request->path(), 1);
                    $pathinfo = explode('/', $pathinfo);
                    $controller = !empty($pathinfo[1]) ? str_replace('.html', '', $pathinfo[1]) : 'index';
                    $action = !empty($pathinfo[2]) ? str_replace('.html', '', $pathinfo[2]) : 'index';
                    $uriArr = [$app, $controller, $action];
                    if (preg_match('/add/', $uriArr[2]) || preg_match('/edit/', $uriArr[2]) || (isset($pathinfo[2]) && $pathinfo[2] == '{id}')) {
                        $uriArr[2] = 'index';
                    }

                    if ($level == 0) {
                        if ($menu['parent']) {
                            $icon = !empty($menu['icon']) ? $menu['icon'] : 'fa fa-caret-right';
                        } else {
                            $icon = $menu['icon'];
                        }
                        if (($uriArr[0] == strtolower($menu['app']) && $uriArr[1] == strtolower($menu['controller']) && $uriArr[2] == strtolower($menu['action']))) {
                            $active = 'layui-this';
                        } else {
                            $active = '';
                        }
                        if (empty($menu['items'])) {
                            $output .= '<li class="layui-nav-item ' . $active . '" data-name="' . $menu['name'] . '">';
                            $output .= '<a href="' . $menu['url'] . '" lay-tips="' . $menu['name'] . '" target="' . $menu['target'] . '">';
                            //$output .= '<a lay-href="' . $menu['url'] . '" lay-tips="' . $menu['name'] . '" target="' . $menu['target'] . '">';
                            $output .= '<i class="layui-icon ' . $icon . '"></i>';
                            $output .= '<cite>' . __($menu['name']) . '</cite>';
                            $output .= '</a>';
                            $output .= '</li>';
                        } else {
                            $current = Menu::where(['app' => $uriArr[0], 'controller' => $uriArr[1], 'action' => $uriArr[2], 'status' => 1])->find();
                            if ($current) {
                                $parentIds = $this->getParentIds($current['parent_id']);
                                $parentIdsArr = explode(',', $parentIds);
                                if (in_array($menu['id'], $parentIdsArr)) {
                                    $class = 'layui-nav-itemed';
                                } else {
                                    $class = '';
                                }
                            } else {
                                $class = '';
                            }
                            $output .= '<li class="layui-nav-item ' . $class . '" data-name="' . $menu['name'] . '">';
                            $output .= '<a href="javascript:;" lay-tips="' . $menu['name'] . '">';
                            $output .= '<i class="layui-icon ' . $icon . '"></i>';
                            $output .= '<cite>' . __($menu['name']) . '</cite>';
                            $output .= '</a>';
                            $output .= '<dl class="layui-nav-child">';
                            $output .= $subMenu($menu['items'], $level + 1);
                            $output .= '</dl>';
                            $output .= '</li>';
                        }
                    } else {
                        if (($uriArr[0] == strtolower($menu['app']) && $uriArr[1] == strtolower($menu['controller']) && $uriArr[2] == strtolower($menu['action']))) {
                            $active = 'layui-this';
                        } else {
                            $active = '';
                        }
                        $current = Menu::where(['app' => $uriArr[0], 'controller' => $uriArr[1], 'action' => $uriArr[2], 'status' => 1])->find();
                        if ($current) {
                            $parentIds = $this->getParentIds($current['parent_id']);
                            $parentIdsArr = explode(',', $parentIds);
                            if (in_array($menu['id'], $parentIdsArr)) {
                                $active = 'layui-this';
                                $class = 'layui-nav-itemed';
                            } else {
                                $class = '';
                            }
                        } else {
                            $class = '';
                        }
                        if (empty($menu['items'])) {
                            $output .= '<dd class="' . $active . '">';
                            $output .= '<a href="' . $menu['url'] . '" target="' . $menu['target'] . '">';
                            //$output .= '<a lay-href="' . $menu['url'] . '" target="' . $menu['target'] . '">';
                            $output .= __($menu['name']);
                            $output .= '</a>';
                            $output .= '</dd>';
                        } else {
                            $output .= '<dd class=" ' . $class . '" data-name="' . $menu['name'] . '">';
                            $output .= '<a href="javascript:;" lay-tips="' . $menu['name'] . '">';
                            $output .= __($menu['name']);
                            $output .= '</a>';
                            $output .= '<dl class="layui-nav-child">';
                            $output .= $subMenu($menu['items'], $level + 1);
                            $output .= '</dl>';
                            $output .= '</dd>';
                        }
                    }
                }
                return $output;
            };
            $output = $subMenu($menus);
        }

        return $output;
    }

    // 得到父辈、爷辈、祖辈...的集合
    public function getParentIds($parentId, $level = 0)
    {
        $recursive = function ($parentId, $level = 0) use (&$recursive) {
            $result = Menu::where(['id' => $parentId])->field('id, parent_id')->find();
            if ($result) {
                $parentIds = $result['id'];

                if ($result['parent_id']) {
                    $level = $level + 1;
                    $parentIds .= ',' . $recursive($result['parent_id'], $level);
                }
            } else {
                $parentIds = '';
            }

            return $parentIds;
        };

        return $recursive($parentId, $level);
    }

    public function getLocation()
    {
        // BCN
        //$bcn = '';
        if ($this->menu) {
            // set page title with translation
            $trans_title_key = strtolower(str_replace(' ', '_', $this->menu->name));
            if (__($trans_title_key) == $trans_title_key) {
                View::assign('title', $this->menu->name);
            } else {
                View::assign('title', __($trans_title_key));
            }

            // set bcn by Closure menu
            $recursive = function ($id, $output) use (&$recursive) {
                $this->rootline[] = $id;
                $menu = Menu::find($id);
                $trans_title_key = strtolower(str_replace(' ', '_', $menu->name));
                $title = $menu->name;
                if (__($trans_title_key) != $trans_title_key) {
                    $title = __($trans_title_key);
                }
                $link = '';
                if ($this->menu->id == $id) {
                    $link .= '<a><cate>' . $title . '</cate></a>';
                } else {
                    //$menu->route = strtolower($menu->app . '/' . $menu->controller . '/' . $menu->action);
                    //$link .= '<a href="' . url($menu->route) . '"><cate>' . $title . '</cate></a>';
                    $link .= '<a><cate>' . $title . '</cate></a>';
                }

                array_unshift($output, $link);

                if ($menu->parent_id) {
                    $output = $recursive($menu->parent_id, $output);
                }
                return $output;
            };

            // TODO
            //$bcn = view('/common/bcn', ['breadcrumbs' => $recursive($this->menu->id, [])]);
            $bcn = '';

            return $bcn;
        }
    }

    /**
     * Show
     *
     * @param string $template
     * @param array $data
     * @return mixed
     * @throws \Exception
     */
    private function show($template, $data)
    {
        return view(strtolower($this->model) . '/' . $template, $data);
    }

    /**
     * List view
     * @return mixed
     */
    public function index(Request $request)
    {
        $param = $request->all();

        if ($request->isAjax()) {
            $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
            $model = new $className;

            // 每页起始条数
            $start = ($param['page'] - 1) * $param['limit'];
            // 每页显示条数
            $length = $param['limit'];
            // 排序条件
            $order = '';

            $where = $this->getFilterWhere($request);
            if ($this->defaultWhere) {
                $where = array_merge($this->defaultWhere, $where);
            }
            if ($this->defaultOrder) {
                $order = $this->defaultOrder;
            }

            $data = $model->where($where)->order($order)->limit($start, $length)->select();
            $count = $model->where($where)->count();

            if ($data) {
                // 处理列表数据
                $data = $this->handleListData($data);
            }

            $result = [
                'code' => '0',
                'msg' => '',
                'data' => $data,
                'count' => $count,
            ];

            return json($result);
        }

        return self::show($this->indexView, [
            'primaryKey' => $this->primaryKey,
            'params' => $param
        ]);
    }

    /**
     * Add
     *
     * @param Request $request
     * @return mixed
     * @throws \Exception
     */
    public function add(Request $request)
    {
        return self::show($this->addView, [
            'primaryKey' => $this->primaryKey,
        ]);
    }

    /**
     * Add Post
     *
     * @param Request $request
     * @param string $redirect
     * @return mixed
     */
    public function addPost(Request $request)
    {
        return $this->savePost($request);
    }

    /**
     * Edit
     *
     * @param Request $request
     * @return mixed
     */
    public function edit(Request $request)
    {
        $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
        $model = new $className;
        $id = $request->input('id');
        $info = $model->find($id);
        if (!$info) {
            return redirect(url('/' . $this->model . '/index'));
        }
        $info = $model->formatItem($info, ['source' => 'backend']);

        // 公共模板变量
        View::assign('info', $info); // object param reference will be changed by function

        return self::show($this->editView, [
            'primaryKey' => $this->primaryKey,
        ]);
    }

    /**
     * Edit Post
     *
     * @param Request $request
     * @param int $id
     * @param string $redirect
     * @return mixed
     */
    public function editPost(Request $request)
    {
        return $this->savePost($request);
    }

    /**
     * Save Post
     */
    protected function savePost($request)
    {
        $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
        $model = new $className;

        if ($request->isPost()) {
            Db::startTrans();
            try {
                $data = self::evalData($request);

                if ($data['id']) {
                    // Update data
                    $data['updated_at'] = time();
                    $data['updated_id'] = $this->admin['id'];
                    $result = $model->where('id', $data['id'])->update($data);

                    $msg = '保存成功!';
                    $logInfo = $this->admin['name'] . '更新了一条' . __($this->model) . '数据。';
                    $afterAction = '_after_update';
                } else {
                    // Insert data
                    $data['created_at'] = time();
                    $data['created_id'] = $data['updated_id'] = $this->admin['id'];
                    $result = $model->create($data);

                    $msg = '添加成功!';
                    $logInfo = $this->admin['name'] . '添加了一条' . __($this->model) . '数据。';
                    $afterAction = '_after_insert';
                }

                if ($result !== false) {
                    // Query执行后的操作
                    $model->$afterAction($data);

                    // 写入日志
                    Common::adminLog($request, $logInfo);

                    Db::commit();
                    return $this->success($msg, url('/' . $this->model . '/index'));
                } else {
                    Db::rollback();
                    return $this->error('保存失败!', url('/' . $this->model . '/index'));
                }
            } catch (Exception $e) {
                Db::rollback();
                return $this->error($e->getMessage());
            }
        } else {
            return $this->error('请求方式错误!');
        }
    }

    /**
     * Activate
     *
     * @param Request $request
     * @return mixed
     */
    public function activate(Request $request)
    {
        $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
        $model = new $className;

        $id = $request->input('id');
        if (isset($_POST['ids'])) {
            // Activate 多条
            $ids = $request->input('ids');
            $primaryKey = $this->primaryKey;
            $result = $model->whereIn($primaryKey, $ids)->update(['status' => 0, 'updated_id' => $this->admin['id'], 'updated_at' => time()]);
        } elseif ($id) {
            // Activate 一条
            $info = $model->find($id);
            if (!$info) {
                return $this->error('数据不存在!');
            }
            $result = $info->update(['status' => 0, 'updated_id' => $this->admin['id'], 'updated_at' => time()]);
        }

        if ($result !== false) {
            // 写入日志
            $logInfo = $this->admin['name'] . '显示了一条' . __($this->model) . '数据。';
            Common::adminLog($request, $logInfo);

            return $this->success('操作成功!', url('/' . $this->model . '/index'));
        } else {
            return $this->error('操作失败!', url('/' . $this->model . '/index'));
        }
    }

    /**
     * Deactivate
     *
     * @param Request $request
     * @return mixed
     */
    public function deactivate(Request $request)
    {
        $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
        $model = new $className;

        $id = $request->input('id');
        if (isset($_POST['ids'])) {
            // Deactivate 多条
            $ids = $request->input('ids');
            $primaryKey = $this->primaryKey;
            $result = $model->whereIn($primaryKey, $ids)->update(['status' => 1, 'updated_id' => $this->admin['id'], 'updated_at' => time()]);
        } elseif ($id) {
            // Deactivate 一条
            $info = $model->find($id);
            if (!$info) {
                return $this->error('数据不存在!');
            }
            $result = $info->update(['status' => 1, 'updated_id' => $this->admin['id'], 'updated_at' => time()]);
        }

        if ($result !== false) {
            // 写入日志
            $logInfo = $this->admin['name'] . '隐藏了一条' . __($this->model) . '数据。';
            Common::adminLog($request, $logInfo);

            return $this->success('操作成功!', url('/' . $this->model . '/index'));
        } else {
            return $this->error('操作失败!', url('/' . $this->model . '/index'));
        }
    }

    /**
     * Delete
     *
     * @param Request $request
     * @return mixed
     */
    public function delete(Request $request)
    {
        $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
        $model = new $className;

        //$primaryKey = $request->param('primaryKey') ? $request->param('primaryKey') : 'id';
        $primaryKey = $this->primaryKey;
        Db::startTrans();
        try {
            $id = $request->input('id');
            if (isset($_POST['ids'])) {
                // 删除多条
                $ids = $request->input('ids');
                $result = $model->whereIn($primaryKey, $ids)->delete();
            } else if ($id) {
                // 删除一条
                $info = $model->find($id);
                if (!$info) {
                    Db::rollback();
                    return $this->error('数据不存在!');
                }
                if (method_exists($model, 'isUsed')) {
                    // 是否被使用
                    $usedInfo = $model->isUsed($id);
                    if ($usedInfo['code'] == 0) {
                        Db::rollback();
                        return $this->error($usedInfo['msg']);
                    }

                    if ($usedInfo['code'] == 1) {
                        //$result = $model->where($this->primaryKey, $id)->delete(); // 直接删除会查找不到该条数据
                        $info = $model->where($this->primaryKey, $id)->find();
                        $result = $info->delete();
                    } elseif ($usedInfo['code'] == 2) {
                        $result = $model->where($this->primaryKey, $id)->update(['status' => 90]);
                    }
                } else {
                    $info = $model->find($id);
                    if (!$info) {
                        Db::rollback();
                        return $this->error('数据不存在!');
                    }
                    $result = $info->delete();
                }
            }

            if ($result !== false) {
                // Query执行后的操作
                $model->_after_delete($info ?? $id);

                // 写入日志
                $logInfo = $this->admin['name'] . '删除了一条' . __($this->model) . '数据。';
                Common::adminLog($request, $logInfo);

                Db::commit();
                return $this->success('删除成功!', url('/' . $this->model . '/index'));
            } else {
                Db::rollback();
                return $this->error('删除失败!', url('/' . $this->model . '/index'));
            }
        } catch (Exception $e) {
            Db::rollback();
            return $this->error($e->getMessage());
        }
    }

    /**
     * 排序
     *
     * @param Request $request
     * @param string $sortField 自定义排序字段 默认为sorting
     * @return mixed
     */
    public function listOrders(Request $request, $sortField = 'sorting')
    {
        $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
        $model = new $className;

        Db::startTrans();
        try {
            $param = $request->all();
            $ids = $param['sorting_ids'];
            $fields = $param[$sortField];
            $result = true;
            foreach ($ids as $key => $id) {
                $data[$sortField] = $fields[$key];
                $res = $model->where(['id' => $id])->update($data);
                if ($res === false) {
                    $result = false;
                    break;
                }
            }

            if ($result !== false) {
                Db::commit();
                return json(['code' => 1, 'msg' => '排序成功!']);
            } else {
                Db::rollback();
                return json(['code' => 1, 'msg' => __('sorting.fail')]);
            }
        } catch (Exception $e) {
            Db::rollback();
            return $this->error($e->getMessage());
        }
    }

    /**
     * Eval data for post data save
     *
     * @param Request $request
     * @return array
     */
    protected function evalData($request)
    {
        $data = $request->all();
        //$className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
        //$model = new $className;

        // Validate field
        if (!empty($this->validate)) {
            $validateName = '\\app\\' . $this->appName . '\\validate\\' . $this->model;
            if (class_exists($validateName)) {
                $validate = new $validateName;
                $validate->check($this->validate);
                //$this->validate($this->validate, $model);
            }
        }

        // 去掉文件上传字段file
        unset($data['file']);

        return $data;
    }

    /**
     * 处理列表数据
     */
    protected function handleListData($data)
    {
//        $className = '\\app\\' . $this->appName . '\\model\\' . $this->model;
//        $model = new $className;
//
//        $data = $model->formatDatas($data);

        return $data;
    }

    /**
     * 筛选条件
     */
    protected function getFilterWhere($request)
    {
        $where = [];

        return $where;
    }

    /**
     * 默认条件
     */
    protected function getDefaultWhere()
    {
        $defaultWhere = [
            //['status', '<>', 90] // model 全局的查询范围
        ];

        return $defaultWhere;
    }

    /**
     * AjaxSessionLogout
     */
    public function ajaxSessionLogout(Request $request)
    {
        return json($this->admin['auto_logout']);
    }
}

?>
